use ::core::mem::MaybeUninit;
use ed25519_dalek_blake3::{Keypair, PublicKey, SecretKey, Signer, Verifier};
use hashbrown::HashSet;
use js_sys::Uint8Array;
use rand::{rngs::OsRng, RngCore};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn rmw_utf8_encode(input: &[u8]) -> Vec<u8> {
  rmw_utf8::encode(input)
}

#[wasm_bindgen]
pub fn rmw_utf8_decode(input: &[u8]) -> String {
  rmw_utf8::decode(input)
}

#[wasm_bindgen]
pub struct KeySet(HashSet<[u8; 32]>);

#[wasm_bindgen]
impl KeySet {
  pub fn new() -> KeySet {
    KeySet(HashSet::with_capacity(16))
  }
  pub fn add(&mut self, key: &[u8]) -> bool {
    self.0.insert(key.try_into().unwrap())
  }
  pub fn delete(&mut self, key: &[u8]) -> bool {
    self.0.remove(key)
  }

  #[wasm_bindgen(method, getter, structural)]
  pub fn size(&self) -> usize {
    self.0.len()
  }
}

#[wasm_bindgen]
pub struct Ed25519Secret(Keypair);

#[wasm_bindgen]
pub struct Ed25519Public(PublicKey);

#[wasm_bindgen]
impl Ed25519Secret {
  pub fn new() -> Ed25519Secret {
    let mut csprng = OsRng {};
    let mut seed: [u8; 32] = unsafe { MaybeUninit::uninit().assume_init() };
    csprng.fill_bytes(&mut seed);
    Ed25519Secret::load(&seed)
  }
  pub fn dump(&self) -> Uint8Array {
    Uint8Array::from(&self.0.secret.as_bytes()[..])
  }

  pub fn load(bytes: &[u8]) -> Ed25519Secret {
    let sk = SecretKey::from_bytes(bytes).unwrap();
    let pk: PublicKey = (&sk).into();
    Ed25519Secret(Keypair {
      public: pk,
      secret: sk,
    })
  }

  pub fn public(&self) -> Ed25519Public {
    Ed25519Public(self.0.public)
  }

  pub fn sign(&self, msg: &[u8]) -> Uint8Array {
    Uint8Array::from(&self.0.sign(msg).to_bytes()[..])
  }

  pub fn verify(&self, msg: &[u8], sign: &[u8]) -> bool {
    match self.0.verify(msg, &sign.try_into().unwrap()) {
      Ok(_) => true,
      _ => false,
    }
  }
}

#[wasm_bindgen]
impl Ed25519Public {
  pub fn dump(&self) -> Uint8Array {
    Uint8Array::from(&self.0.as_bytes()[..])
  }

  pub fn load(bytes: &[u8]) -> Ed25519Public {
    Ed25519Public(PublicKey::from_bytes(bytes).unwrap())
  }

  pub fn verify(&self, msg: &[u8], sign: &[u8]) -> bool {
    match self.0.verify(msg, &sign.try_into().unwrap()) {
      Ok(_) => true,
      _ => false,
    }
  }
}
